global without sharing class SchedulerClass implements Schedulable {
    
    global boolean isTest {get;set;}
    
    global void execute(SchedulableContext sc) {
        /** Collect LeadRecords to process */
        if (isTest == null) isTest = false;
        List<QueueSetting__c> qsList;
        Map<Id,QueueSetting__c> qsToQId = new Map<Id,QueueSetting__c>();
        Map<Id,QueueSetting__c> qsToId = new Map<Id,QueueSetting__c>();
        List<String> allQueueIds = new List<String>();
        List<Id> qsIds = new List<Id>();
        List<Id> queueToProcessIds = new List<Id>();
        List<sObject> allRecordsToProcess = new List<sObject>();
        Map<Id,QueuePrioritizationRule__c> qprToQsId = new Map<Id,QueuePrioritizationRule__c>();
        List<QueuePrioritizationRule__c> qprList;
        Map<Id,List<QueuePrioritizationRule__c>> rulesToQueue = new Map<Id,List<QueuePrioritizationRule__c>>();
        
        Map<String,EZAssignSettings__c> assignmentSettingsToObjType = new Map<String,EZAssignSettings__c>();
        List<EZAssignSettings__c> assSetList = [Select ObjectAPIName__c, ActiveFieldValues__c, ActiveFieldAPIName__c From EZAssignSettings__c limit 100];
        for (EZAssignSettings__c item: assSetList) {
            assignmentSettingsToObjType.put(item.ObjectAPIName__c,item);
        }
       
        
        if (sc != null) {
            system.abortJob(sc.getTriggerId());
        }
        
        qsList = [select Id,QueueId__c,Active__c
                  from QueueSetting__c];
        for (QueueSetting__c item: qsList) {
            qsToQId.put(item.QueueId__c,item);
            qsToId.put(item.Id,item);
            qsIds.add(item.Id);
        }
        
        List<QueueSobject> allQueues = [select id, QueueId, SobjectType 
                                        from QueueSobject
                                        where QueueId in :qsToQId.keySet()
                                        limit 5000];
        Map<Id,QueueSobject> queueToId = new Map<Id,QueueSobject>();
        for (QueueSobject queue: allQueues) {
            allQueueIds.add(((String)queue.QueueId).substring(0,15));
            queueToId.put(queue.QueueId,queue);
        }
        
        Integer limitsCount = Math.round(42000 / allQueues.size());
        
        for (Id item: allQueueIds) {
            if (qsToQId.get(item) != null) {
                if (qsToQId.get(item).Active__c) {
                    queueToProcessIds.add(item);
                }
            }
        }
        qprList = [select Id, QueueSettingId__c, ExecutionOrder__c, FieldAPIName__c from QueuePrioritizationRule__c where QueueSettingId__c in :qsIds];
        for (QueuePrioritizationRule__c item: qprList) {
            if (rulesToQueue.get(qsToId.get(item.QueueSettingId__c).QueueId__c) == null) {
                rulesToQueue.put(qsToId.get(item.QueueSettingId__c).QueueId__c,new List<QueuePrioritizationRule__c>());
            }
            List<QueuePrioritizationRule__c> tmpList = rulesToQueue.get(qsToId.get(item.QueueSettingId__c).QueueId__c);
            tmpList.add(item);
            rulesToQueue.put(qsToId.get(item.QueueSettingId__c).QueueId__c,tmpList);
        }
        
        String queryStr;
        
        /** get list of sObjects from all queues to process */
        for (Id item: queueToProcessIds) {
            List<QueuePrioritizationRule__c> priorRules = rulesToQueue.get(item);
            QueuePrioritizationRule__c buf;
            Integer i,j;
            String priorityString = '';
            
            /* ==== get prioritization rules string ==== */
            if (priorRules != null) {
                /* sort prioritization rules by order */
                for (i=0; i<priorRules.size(); i++) {
                    for (j=0; j<priorRules.size()-i-1; j++) {
                        if (priorRules[j].ExecutionOrder__c > priorRules[j+1].ExecutionOrder__c) {
                            buf = priorRules[j];
                            priorRules[j] = priorRules[j+1];
                            priorRules[j+1] = buf;
                        }
                    }
                }
                /* compile prioritization rules string */
                for (i=0; i<priorRules.size(); i++) {
                    if (priorRules[i].FieldAPIName__c != null) {
                        priorityString += ',' + priorRules[i].FieldAPIName__c;
                    }
                }
                if (priorityString != null) {
                    priorityString = ' order by ' + priorityString.substring(1) + ' asc';
                }
            }
            
            queryStr = 'select Id,OwnerId';
            Boolean isHaveSettings = true;
            if (assignmentSettingsToObjType.get(queueToId.get(item).SobjectType) == null) {
                isHaveSettings = false;
            }
            String activeValue;
            if (isHaveSettings) {
                activeValue = assignmentSettingsToObjType.get(queueToId.get(item).SobjectType).ActiveFieldValues__c;
            }
            Boolean isInt = false;
            Boolean isDec = false;
                
            Integer intPart;
            Decimal decPart;
            try {
                decPart = Decimal.valueOf(activeValue);
                if ((decPart + '') == activeValue) {
                    isDec = true;
                }
            } catch (Exception ex) {
                
            }
            if (isHaveSettings) {
                queryStr += ',' + assignmentSettingsToObjType.get(queueToId.get(item).SobjectType).ActiveFieldAPIName__c;
            }
            queryStr += ' from ' + queueToId.get(item).SobjectType + ' where ';
            if (isHaveSettings) {
                List<String> valueParts = activeValue.split(',');
                String valuesFilter = '';
                for (String val: valueParts) {
                    if (valuesFilter != '') {
                        valuesFilter += 'or';
                    }
                    String valToIns = val;
                    if (val != 'true' && val != 'false' && !isDec) {
                        valToIns = '\'' + valToIns + '\'';
                    }
                    valuesFilter += ' (' + assignmentSettingsToObjType.get(queueToId.get(item).SobjectType).ActiveFieldAPIName__c + ' = ' + valToIns + ') ';
                }
                if (valuesFilter != '') {
                    queryStr += ' (' + valuesFilter + ') AND ';
                }
            }
            if (isTest) queryStr += '>>';
            queryStr += ' (OwnerId = \'' + item + '\')';
            if (priorityString != '') {
                queryStr += priorityString;
            }
            queryStr += ' limit ' + limitsCount;
            try {
                List<sObject> currentRecordsList = database.query(queryStr);
                allRecordsToProcess.addAll(currentRecordsList);
            } catch (Exception ex) {
                queryStr = 'select Id,OwnerId';
                isHaveSettings = true;
                if (assignmentSettingsToObjType.get(queueToId.get(item).SobjectType) == null) {
                    isHaveSettings = false;
                }
                
                activeValue = assignmentSettingsToObjType.get(queueToId.get(item).SobjectType).ActiveFieldValues__c;
                isInt = false;
                isDec = false;
                
                try {
                    decPart = Decimal.valueOf(activeValue);
                    if ((decPart + '') == activeValue) {
                        isDec = true;
                    }
                } catch (Exception exc) {
                    
                }
                if (activeValue.toLowerCase() != 'true' && activeValue.toLowerCase() != 'false' && !isDec) {
                    activeValue = '\'' + activeValue + '\'';
                }
                
                if (isHaveSettings) {
                    queryStr += ',' + assignmentSettingsToObjType.get(queueToId.get(item).SobjectType).ActiveFieldAPIName__c;
                }
                queryStr += ' from ' + queueToId.get(item).SobjectType + ' where ';
                if (isHaveSettings) {
                    queryStr += ' (' + assignmentSettingsToObjType.get(queueToId.get(item).SobjectType).ActiveFieldAPIName__c + ' = ' + activeValue + ') AND ';
                }
                queryStr += ' (OwnerId = \'' + ((String)item).substring(0,15) + '\')';
	            queryStr += ' limit ' + limitsCount;
                
                List<sObject> currentRecordsList = database.query(queryStr);
                allRecordsToProcess.addAll(currentRecordsList);
            }
        }
        
        /** Process the list of records */
        if (allRecordsToProcess.size() > 0) {
            if (isTest == null) isTest = false;
            
            try
            {
            	ProcessRecords batchController = new ProcessRecords(allRecordsToProcess,isTest,false);
            	database.executeBatch(batchController,50);
            }
            catch(Exception e)
            {
            	System.debug('Error: Unable able to execute batch job. ' + e);
            }
            
        }
        
    }
    
}